#pragma once
#include <utility>
#include <gsl/gsl_roots.h>
#include <gsl/gsl_integration.h>

class ProbabilityDistribution;

struct RootInfo
{
	ProbabilityDistribution* distribution;
	double u;
};

class ProbabilityDistribution
{
public:
	ProbabilityDistribution(void);
	virtual ~ProbabilityDistribution(void);

	virtual double density(double x) const = 0;
	
	//Default behavior is simply to integrate the density function. 
	virtual double cdf(double x);
	//Default behavior is to use a root finding scheme to find the quantile. 
	virtual double quantile(double u);

	virtual double probability(double a, double b) = 0;
	
	virtual double mean() const { return _mean; }
	virtual double variance() const { return _variance; }
	
	virtual double zeroQuantileLocation(); 
	virtual std::pair<double,double> getSupport() const = 0;
	virtual std::pair<double,double> quantileBounds(double u) const = 0;

protected:
	// It is the responsibility of the concrete classes to initialize these members
	double _mean, _variance, _zeroQuantileLocation; 

	gsl_root_fsolver *_solver;
	gsl_integration_workspace * _integrationWorkspace;

	// Used for gsl's integration callback mechanism
	static double densityWrapper(double x, void *params)
	{
		return static_cast<ProbabilityDistribution*>(params)->density(x);
	}
	// Used for gsl's root finding callback mechanism
	static double cdfWrapper(double x, void *params)
	{
		RootInfo* info = static_cast<RootInfo*>(params);
		return info->distribution->cdf(x) - info->u;
	}
};

